<?php
/**
 * 后台代理Customer商品的管理
 *
 * Author: lyun
 * Date: 2018/3/30
 * Created by Panxsoft.
 */

namespace App\Http\Controllers\Admin\Customer\Product;

use App\Http\Controllers\ApiController;
use App\Http\Requests\Admin\Customer\Product\ProductIndexRequest;
use App\Http\Requests\Admin\Customer\Product\ProductStatusRequest;
use App\Http\Requests\Admin\Customer\Product\StoreProductIndexRequest;
use App\Http\Requests\Admin\Panx\Product\ProductCreateInfoRequest;
use App\Http\Requests\Admin\Customer\Product\ProductStoreRequest;
use App\Http\Requests\Admin\Customer\Product\ProductUpdateRequest;
use App\Models\Admin\CosmeticMaterial;
use App\Models\Admin\CustomerAdmin;
use App\Models\Customer\Asset;
use App\Models\Customer\Customer;
use App\Models\Panx\BodyPosition;
use App\Models\Panx\Brand;
use App\Models\Panx\BrandSeries;
use App\Models\Panx\Category;
use App\Models\Panx\Product;
use App\Models\Panx\ProductMod;
use App\Models\Panx\ProductSku;
use App\Models\Panx\ProductUrlType;
use App\Models\Panx\Sex;
use App\Services\ExcelExporters\ProductExporter;
use App\Services\Locale\Language;
use App\Models\Customer\Language as ModelLanguage;
use App\Services\Repositories\AssetRepository;
use App\Services\Repositories\Product\ProductRepository;
use App\Services\Repositories\Product\ProductTagRepository;
use App\Services\Utils\Guard;
use App\Services\Utils\Transaction;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;

class ProductController extends ApiController
{
    const ROUTES = [
        'index'   => 'admin.customer.product.index',
        'create'  => 'admin.customer.product.create',
        'store'   => 'admin.customer.product.store',
        'show'    => 'admin.customer.product.show',
        'update'  => 'admin.customer.product.update',
        'destroy' => 'admin.customer.product.destroy',

        'patchStatus'       => 'admin.customer.product.patch-status',
        'indexStoreProducts' => 'admin.customer.store.product.index',
    ];

    const DEFAULT_ORDER_BYS = [
        ['order_by' => 'created_at', 'sort' => 'desc'],  // 默认的排序规则
    ];

    protected $productRepository;

    public function __construct(ProductRepository $productRepository)
    {
        $this->productRepository = $productRepository;
    }
    
    /**
     *  代理客户自己上传的商品列表
     * Display a listing of the resource.
     * @test
     *
     * @param ProductIndexRequest $request
     *
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function index(ProductIndexRequest $request)
    {
        $query_products = $this->customerProductQuery($request);

        /** @var Collection|LengthAwarePaginator $products */
        $products = $query_products->paginate($request->per_page ?: 15);

        $this->productRepository->transformProducts($products);

        return $this->response(compact('products'));
    }

    /**
     *  导出商品列表
     *
     * @param ProductIndexRequest $request
     * @throws \Exception
     */
    public function exportCustomerProduct(ProductIndexRequest $request)
    {
        $query_products = $this->customerProductQuery($request);

        // 使用 select 与 without 与不使用区别不太大，主要时间消耗还是在 export 中
        $products = $query_products
            ->select([
                'id', 'title', 'spu_code', 'brand_id', 'category_id', 'brand_series_id', 'priority',
                'sell_price', 'market_price', 'try_on', 'is_sale', 'created_at'
            ])
            ->without([
                'thumbImage', 'colorThumbImage', 'asset',
                'cosmeticMaterial', 'bodyPositions'
            ])
            ->get();

        (new ProductExporter)->export($products, trans('admin.customer_product'));
    }

    /**
     * 代理客户下店铺上传的商品列表
     * @test
     *
     * @param StoreProductIndexRequest $request
     *
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function indexStoreProducts(StoreProductIndexRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(CustomerAdmin::isGuardSuperAdmin(), trans('admin.is_super_man'));

        /** @var Customer $customer */
        $customer = Guard::customer();

        $products = $customer->storeProducts()
            ->columnsKeywordsOrLike([
                'title' => $request->keyword,
            ])
            ->filterStoreId($request->store_id)
            ->filterBrand($request->brand_id)
            ->filterCategory($request->category_id)
            ->filterIsSale($request->is_sale)
            ->filterIsDeleted($request->is_deleted)
            ->filterIsHot($request->is_hot)
            ->filterIsNew($request->is_new)
            ->orderBys($request->order_bys, self::DEFAULT_ORDER_BYS)
            ->with(['category', 'thumbImage', 'colorThumbImage', 'asset', 'productTags', 'categoryDescriptionByLanguage'])
            ->paginate($request->per_page ?: 15);

        return $this->response(compact('products'));
    }

    /**
     * Show the form for creating a new resource.
     *
     * @param AssetRepository $assetRepo
     *
     * @param ProductTagRepository $productTagRepo
     *
     * @return \Illuminate\Http\Response
     */
    public function create(AssetRepository $assetRepo,
                           ProductTagRepository $productTagRepo)
    {
        /** @var CustomerAdmin $customer_admin */
        $customer_admin = Guard::customerAdmin();
        $language_id = ModelLanguage::mapCodeToId($customer_admin->language);

        /** @var Customer $customer */
        $customer = $customer_admin->customer;
        $customer_id = $customer->id;

        // 客户开通的商品类别
        $categories = $customer->getCustomerCategories($language_id);
        $group_categories = Category::groupCategoryByCustomType($categories);

        $brands = (new Brand)
            ->where('customer_id', $customer_id)
            ->latest()
            ->pluck('name', 'id');

        $brand_with_series = $customer->getBrandsWithSeries();

        $product_tags = $productTagRepo->getProductTags();

        $assets = Asset::filterCustomerNoStore($customer_id)
            ->latest()
            ->get();

        $assets_group = $assetRepo->groupAssetsByCategory($assets);

        $product_url_types = (new ProductUrlType)->latest('priority')->get(['id', 'const', Language::getLocalNameColumn()]);

        $stores = Guard::customer()->stores()->get(['id', 'name']);

        $sexes = Sex::all(['id', Language::getLocalNameColumn()]);

        $body_positions = BodyPosition::all(['id', Language::getLocalNameColumn()]);

        /** @deprecated 去掉商品材质信息 */
        $cosmetic_materials = CosmeticMaterial::filterCustomer($customer_id)->get(['id', 'name']);

        return $this->response(compact(
            'categories', 'brands', 'product_tags',
            'assets', 'product_url_types', 'stores', 'assets_group',
            'sexes', 'body_positions', 'cosmetic_materials', 'group_categories','brand_with_series'
        ));
    }

    /**
     *  Customer上传商品信息
     * Store a newly created resource in storage.
     * @test
     *
     * @param  ProductStoreRequest $request
     *
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function store(ProductStoreRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(CustomerAdmin::isGuardSuperAdmin(), trans('admin.is_super_man'));

        $product_detail = $this->productRepository->assembleProductDetail($request, Guard::customerAdmin()->customer_id);

        $stored = Transaction::handle(function () use ($request, $product_detail) {

            /** @var Product $product */
            $product = (new Product)->create($product_detail);

            // 保存商品关联部位信息 (deprecated,hm测肤改成自定义推荐商品)
//            $this->productRepository->syncProductPivotBodyPosition($product, $request->body_position_ids);

            // 商品缩略图的上传
            $product->updateThumb($request->thumb);

            // 商品颜色缩略图的上传
            $product->updateColorThumb($request->color_thumb);

            // 关联商品标签的信息
            $this->productRepository->syncProductTags($product, $request->product_tag_ids);

            // 商品属性值、商品规格，商品sku的信息
//            $this->syncProductPivotAttributeValue($request, $product);
//            $this->syncProductPivotSpecValue($request, $product);
//            $this->updateProductSku($request, $product);

            return $product;
        });

        return $this->response(compact('stored'));
    }

    /**
     * Update the specified resource in storage.
     * @test
     *
     * @param  ProductUpdateRequest $request
     * @param  int $id
     *
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function update(ProductUpdateRequest $request, $id)
    {
        // 超管才能操作的
        $this->checkIsTrue(CustomerAdmin::isGuardSuperAdmin(), trans('admin.is_super_man'));

        /** @var Product $product */
        $product = (new Product)
            ->where('customer_id', Guard::customerAdmin()->customer_id)
            ->where('store_id', Product::STORE_ID_CUSTOMER)
            ->find($id);
        $this->checkItemExist($product, trans('admin.product'));

        $product_detail = $this->productRepository->assembleProductDetail($request, Guard::customerAdmin()->customer_id);

        $product_detail = $this->productRepository->unsetUpdateProductDetail($product, $product_detail);

        $updated = Transaction::handle(function () use ($request, $product_detail, $product) {

            $product->update($product_detail);

            // 保存商品关联部位信息 (deprecated,hm测肤改成自定义推荐商品)
//            $this->productRepository->syncProductPivotBodyPosition($product, $request->body_position_ids);

            // 商品缩略图的上传
            $product->updateThumb($request->thumb);

            // 商品颜色缩略图的上传
            $product->updateColorThumb($request->color_thumb);

            // 商品标签的更改
            $this->productRepository->syncProductTags($product, $request->product_tag_ids);

            // 商品属性值，规格值，sku的更改
//            $this->syncProductPivotAttributeValue($request, $product);
//            $this->syncProductPivotSpecValue($request, $product);
//            $this->updateProductSku($request, $product);

            return true;
        });

        return $this->response(compact('updated', 'product'));
    }

    /**
     * Remove the specified resource from storage.
     * @test
     *
     * @param  int $id
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function destroy($id)
    {
        // 超管才能操作的
        $this->checkIsTrue(CustomerAdmin::isGuardSuperAdmin(), trans('admin.is_super_man'));

        /** @var Product $product */
        $product = (new Product)
            ->where('customer_id', Guard::customerAdmin()->customer_id)
            ->where('store_id', Product::STORE_ID_CUSTOMER)
            ->find($id);
        $this->checkItemExist($product, trans('admin.product'));

        $deleted = Transaction::handle(function () use ($product) {
            return ProductRepository::destroyCleanRelations($product);
        });

        return $this->response(compact('deleted'));
    }

    /**
     * 修改商品 状态
     * 这个api实际上只做上下架处理
     * @test
     *
     * @param ProductStatusRequest $request
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function patchStatus(ProductStatusRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(CustomerAdmin::isGuardSuperAdmin(), trans('admin.is_super_man'));

        $column = $request->type;
        $value = $request->value;

        /** @var Product $product */
        $product = (new Product)
            ->where('customer_id', Guard::customerAdmin()->customer_id)
            ->where('store_id', Product::STORE_ID_CUSTOMER)
            ->find($request->id);
        $this->checkItemExist($product, trans('admin.product'));

        $this->productRepository->checkSaleCondition($product);

        $updated = $product->update([$column => $value]);

        return $this->response(compact('updated'));
    }

    /**
     * @deprecated (暂时没用了！)
     *  返回 categories 或者 product_model 对应的 attributeValues 和 specValues  的数据
     *
     * @param ProductCreateInfoRequest $request
     * @return \Illuminate\Http\Response
     */
    public function createInfo(ProductCreateInfoRequest $request)
    {
        switch ($request->type) {
            case 'category':
                $data = Category::where('parent_id', $request->id)->pluck('name', 'id')->all();
                break;
            case 'product_model':
                $data = $this->getSpecsAndAttributes($request->id);
                break;
            default:
                $data = false;
        }

        return $this->response(compact('data'));
    }

    /**
     * @param ProductIndexRequest $request
     *
     * @return Product|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\HasMany
     * @throws \Exception
     */
    private function customerProductQuery(ProductIndexRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(CustomerAdmin::isGuardSuperAdmin(), trans('admin.is_super_man'));

        /** @var Customer $customer */
        $customer = Guard::customer();

        $products = $customer->selfProducts()
            ->columnsKeywordOrLike(['title', 'spu_code', 'name'], $request->keyword)
            ->filterBrand($request->brand_id)
            ->filterCategory($request->category_id)
            ->filterIsSale($request->is_sale)
            ->filterIsDeleted($request->is_deleted)
            ->filterIsHot($request->is_hot)
            ->filterIsNew($request->is_new)
            ->filterHasYouzan($request->has_youzan)
            ->with(['category', 'thumbImage', 'colorThumbImage',
                'asset', 'productTags', 'brand', 'brandSeries',
                'cosmeticMaterial', 'bodyPositions', 'categoryDescriptionByLanguage'])
            ->orderBy($request->sort_column ?: 'created_at', $request->sort_by ?: 'DESC');

        return $products;
    }

    /**
     * @param ProductStoreRequest|ProductUpdateRequest|Request $request
     * @param Product $product
     *
     * @return bool
     */
    private function syncProductPivotSpecValue(Request $request, Product $product)
    {
        $req_spec_vals = $request->spec_values;
        if (is_null($req_spec_vals)) {
            return false;
        }

        $spec_val_ids = collect($req_spec_vals)->mapWithKeys(function (array $spec_val) {
            return [
                $spec_val['id'] => [
                    'thumb' => $spec_val['thumb'] ?? null
                ]
            ];
        })->all();

        $product->specValues()->sync($spec_val_ids);
    }

    /**
     * @param ProductStoreRequest|ProductUpdateRequest|Request $request
     * @param Product $product
     *
     * @return bool
     */
    private function syncProductPivotAttributeValue(Request $request, Product $product)
    {
        $req_attr_vals = $request->attribute_values;
        if (is_null($req_attr_vals)) {
            return false;
        }

        $attr_val_ids = array_column($req_attr_vals, 'id');

        $product->attributeValues()->sync($attr_val_ids);
    }

    /**
     * @param ProductStoreRequest|ProductUpdateRequest|Request $request
     * @param Product $product
     *
     * @return bool
     */
    private function updateProductSku(Request $request, Product $product)
    {
        if (is_null($request->product_skus)) return false;

        list(
            $new_skus,
            $update_skus,
            $delete_sku_ids
        ) = $this->extractSkuData($request->product_skus, $product);

        $this->createSkus($new_skus);

        $this->updateSkus($update_skus);

        $this->deleteSkus($delete_sku_ids);

        return true;
    }

    /**
     * 用请求数据与产品已有的 SKU 对比，提取新增 / 更新 / 删除的 sku 数据
     *
     * @param array $request_skus
     * @param Product $product
     * @return array[3]
     */
    private function extractSkuData(array $request_skus, Product $product): array
    {
        $new_skus = [];
        $update_skus = [];

        $request_skus = $this->normalizeRequestSkus($request_skus);

        /** @var ProductSku[]|Collection $exist_skus */
        $exist_skus = $product->productSkus;

        foreach ($request_skus as $request_sku) {
            $store_sku = [
                'spec_value_ids' => json_encode($request_sku['spec_value_ids']),
                'product_id'     => $product->id,
                'sell_price'     => $request_sku['sell_price'] ?? 0,
                'thumb'          => $request_sku['thumb'] ?? '',
                'sku_code'       => $request_sku['sku_code'] ?? '',
            ];

            if ($exist_skus->where('spec_value_ids', $request_sku['spec_value_ids'])->first()) {
                array_push($update_skus, $store_sku);
            } else {
                array_push($new_skus, $store_sku);
            }
        }

        /** @var array $req_spec_value_ids */
        $req_spec_value_ids = array_column($request_skus, 'spec_value_ids');
        $delete_sku_ids = $exist_skus->whereNotIn('spec_value_ids', $req_spec_value_ids)
            ->pluck('id')->all();

        return [$new_skus, $update_skus, $delete_sku_ids];
    }

    /**
     * 把请求的 SKU ID 转成整数并排序（正）
     *
     * @param array|null $product_skus
     * @return array
     */
    private function normalizeRequestSkus(array $product_skus): array
    {
        return array_map(function (array $product_sku) {
            // id string to int
            $product_sku['spec_value_ids'] = array_map(function ($id) {
                return (int)$id;
            }, $product_sku['spec_value_ids']);

            // sort ids by ascend
            $product_sku['spec_value_ids'] = array_values(array_sort($product_sku['spec_value_ids']));

            return $product_sku;
        }, $product_skus);
    }

    /**
     * @param array $skus
     */
    private function createSkus(array $skus)
    {
        if (!count($skus)) return;

        (new ProductSku)->insert($skus);
    }

    /**
     * @param array $skus
     */
    private function updateSkus(array $skus)
    {
        foreach ($skus as $sku) {
            (new ProductSku)
                ->where([
                    'spec_value_ids' => $sku['spec_value_ids'],
                    'product_id'     => $sku['product_id'],
                ])
                ->update($sku);
        }
    }

    /**
     * @param array $ids
     */
    private function deleteSkus(array $ids)
    {
        if (! count($ids)) return;

        ProductSku::destroy($ids);
    }

    /**
     * @param $id
     * @return Collection
     */
    private function getSpecsAndAttributes($id)
    {
        /** @var ProductMod $product_model */
        $product_model = ProductMod::find($id); // 前端检测过了

        /** @var Collection $data */
        $data = $product_model->load('specs.specValues', 'attributes.attributeValues');

        return $data;
    }
}

